1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2025 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2025. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.assets.image; 12 public import hip.api.data.asset; 13 public import hip.api.data.image; 14 15 16 /** 17 * This class represents pixel data on RAM (CPU Powered) 18 * this is useful for loading images on another thread and then 19 * sending it to the GPU 20 */ 21 public class Image : HipAsset, IImage 22 { 23 IHipImageDecoder decoder; 24 string imagePath; 25 int width, height; 26 ubyte bytesPerPixel; 27 ushort bitsPerPixel; 28 29 ubyte[] pixels; 30 31 this(uint width, uint height, ubyte bytesPerPixel) 32 { 33 super("Image"); 34 pixels = new ubyte[width*height*bytesPerPixel]; 35 this.bytesPerPixel = bytesPerPixel; 36 this.height = height; 37 this.width = width; 38 } 39 40 this(string path = "") 41 { 42 import hip.util.system : sanitizePath; 43 path = sanitizePath(path); 44 super("Image_"~path); 45 imagePath = path; 46 } 47 ///Loads an arbitrary buffer which will be decoded by the decoder. 48 this(in string path, in ubyte[] buffer, void delegate(IImage self) onSuccess, void delegate() onFailure) 49 { 50 this(path); 51 loadFromMemory(cast(ubyte[])buffer, onSuccess, onFailure); 52 } 53 54 static immutable(IImage) getPixelImage() 55 { 56 __gshared Image img; 57 __gshared ubyte[4] pixel = IHipImageDecoder.getPixel(); 58 if(img is null) 59 { 60 img = new Image("Pixel"); 61 img.pixels = pixel; 62 img.width = 1; 63 img.height = 1; 64 img.bytesPerPixel = 4; 65 } 66 return cast(immutable)img; 67 } 68 string getName() const {return imagePath;} 69 uint getWidth() const {return width;} 70 uint getHeight() const {return height;} 71 ubyte getBytesPerPixel() const {return bytesPerPixel;} 72 const(ubyte[]) getPalette() const {return decoder ? decoder.getPalette : null;} 73 const(ubyte[]) getPixels() const {return pixels;} 74 75 void loadRaw(in ubyte[] pixels, int width, int height, ubyte bytesPerPixel) 76 { 77 this.width = width; 78 this.height = height; 79 this.pixels = cast(ubyte[])pixels; 80 this.bytesPerPixel = bytesPerPixel; 81 this.bitsPerPixel = cast(ubyte)(bytesPerPixel*8); 82 } 83 84 85 bool loadFromMemory(ubyte[] data, void delegate(IImage self) onSuccess, void delegate() onFailure) 86 { 87 if(data.length == 0) 88 throw new Exception("No data was passed to load Image. Could not load image at path "~imagePath); 89 if(decoder is null) 90 decoder = getDecoder(imagePath); 91 92 if(!(decoder.startDecoding(data, () 93 { 94 width = decoder.getWidth(); 95 height = decoder.getHeight(); 96 bitsPerPixel = decoder.getBitsPerPixel(); 97 bytesPerPixel = decoder.getBytesPerPixel(); 98 pixels = cast(ubyte[])decoder.getPixels(); 99 onSuccess(this); 100 }, onFailure))) 101 throw new Exception("Decoding Image Error: Could not load image "~imagePath); 102 return true; 103 } 104 105 bool hasLoadedData() const {return pixels !is null && width != 0 && height != 0;} 106 107 override bool isReady() const { return hasLoadedData(); } 108 109 ubyte[] monochromeToRGBA() const 110 { 111 ubyte[] pix = new ubyte[](4*width*height); //RGBA for each pixel 112 uint pixelsLength = width*height; 113 ubyte color; 114 uint z; 115 for(uint i = 0; i < pixelsLength; i++) 116 { 117 //Palette r color = palette[pixels[i]*4] 118 color = (cast(ubyte*)pixels)[i]; 119 pix[z++] = color; //R 120 pix[z++] = color; //G 121 pix[z++] = color; //B 122 pix[z++] = color; //A 123 } 124 125 return pix; 126 } 127 128 ubyte[] convertPalettizedToRGBA() const 129 { 130 ubyte[] pix = new ubyte[](4*width*height); //RGBA for each pixel 131 uint pixelsLength = width*height; 132 const(ubyte[]) palette = decoder.getPalette(); 133 134 uint colorIndex; 135 uint z; 136 for(uint i = 0; i < pixelsLength; i++) 137 { 138 //Palette r color = palette[pixels[i]*4] 139 colorIndex = (cast(ubyte*)pixels)[i]*4; 140 pix[z++] = palette[colorIndex]; //R 141 pix[z++] = palette[colorIndex+1]; //G 142 pix[z++] = palette[colorIndex+2]; //B 143 pix[z++] = palette[colorIndex+3]; //A 144 } 145 146 return pix; 147 } 148 149 override void onDispose() {decoder.dispose(); } 150 override void onFinishLoading() { } 151 alias w = width; 152 alias h = height; 153 } 154 155